import json
import os
import pathlib

from collections import deque
from datetime import datetime
import unicodedata

import sys
import os

def get_file_path(filename):
    """自动判断是源码运行还是打包后运行，并返回正确的文件路径"""
    if getattr(sys, 'frozen', False):  # 如果是 .exe 打包后的程序
        base_path = sys._MEIPASS
    else:  # 如果是直接运行 .py 文件
        base_path = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(base_path, filename)


# 货币字典
CurrencyDictionary={
    '000':"CNY",
    '001':"CNY",
    '011':'CNY',
    '002':'AUA',
    '003':'AUB',
    '010':'BDT',
    '012':'GBP',
    '013':'HKD',
    '014':'USD',
    '015':'CHF',
    '016':'DEM',
    '017':'FRF',
    '018':'SGD',
    '019':'PKR',
    '020':'NLG',
    '021':'SEK',
    '022':'DKK',
    '023':'NOK',
    '027':'JPY',
    '028':'CAD',
    '029':'AUD',
    '030':'TZS',
    '031':'ESP',
    '032':'MYR',
    '033':'XEU',
    '034':'XAU',
    '035':'GLD',
    '036':'XAG',
    '037':'XDR',
    '038':'EUR',
    '040':'LKR',
    '041':'DZD',
    '042':'FIM',
    '044':'IQD',
    '046':'MAD',
    '047':'SLL',
    '048':'IRR',
    '049':'NPR',
    '060':'CNY',
    '061':'ALL',
    '063':'KPW',
    '064':'VND',
    '066':'BGL',
    '070':'ZAR',
    '071':'LYD',
    '080':'ZMK',
    '081':'MOP',
    '082':'PHP',
    '084':'THB',
    '085':'INR',
    '087':'NZD',
    '088':'KRM',
    '089':'SYP',
    '094':'USD',
    '095':'CHF',
    '240':'ROL',
    '601':'GHC'
}


# ============分行机构号 =========
# 山西机构号范围
sx_org_begin = 1006
sx_org_end = 1299
# 内蒙机构号范围
nm_org_begin = 1301
nm_org_end = 1599
# 吉林机构号范围
jl_org_begin = 2009
jl_org_end = 2299
# 黑龙江机构号范围
hl_org_begin = 2300
hl_org_end = 2557

# 确定actvhh与rettxn的有效长度
ACTVHH_LEN = 32
RETTXN_LEN = 37

# 获取输出文件名称
outfile_dict = {
    '山西':'RSX000.00000050.DTP',
    '内蒙':'RNM000.00000060.DTP',
    '吉林':'RJL000.00000080.DTP',
    '黑龙江':'RHL000.00000090.DTP'
}
# 获取输出配置文件参数
outconfig = {
}
# 设置文件输出路径
output_path = ''
# 获取输出文件名称
output_file = ''
# 省份对应机构号
addr_to_org = {
    '山西省': '41041',
    '内蒙古省':'41405',
    '吉林省':'42208',
    '黑龙江省':'42465'
}


# 新旧账户映射字典
OLDACCOUNT_NEWACCOUNT_DICT = {}
# RETOBM旧账号集合
RETOBM_OLDACCOUNT_DICT = {}
# 交易码-交易功能描述映射字典
TXNCDE_DICT = {}
# 商户号-商户名称映射字典
MERCH_DICT = {}
# 新账号-核心客户号映射字典
NEW_ACCOUNT_CUSID_DICT = {}
# 机构号-机构号名称映射字典
ORG_DICT = {}
# 账户号和卡表映射字典
OLD_ACCOUNT_CARDNO_DICT = {}
# 设置CDTDLG商户号映射表
CDTDLG_MERCHNO_DICT = {}


# 初始化新旧账号对照表
def INIT_OLDACCOUNT_NEWACCOUNT_DICT() :
    print(f'正在初始化新旧账号对照表。。。。')
    file_path = get_file_path("新旧账号映射表.txt")
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip().split('|')
            if line[0].strip() == '' or line[1].strip() == '' :
                continue
            OLDACCOUNT_NEWACCOUNT_DICT[line[0]] = line[1].strip()
    print(f'成功初始化新旧账号对照表。。。。')
    return

# 初始化RETOBM旧账号映射表 orgidt||depnum||dpttyp||seqnum||chkbit
def INIT_RETOBM_OLDACCOUNT_SET():
    print(f'正在初始化初始化RETOBM旧账号集合。。。。')
    file_path = get_file_path('RETOBM旧账号映射表.txt')
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip().split('|')
            RETOBM_OLDACCOUNT_DICT[line[0]] = line[1]
    print(f'完成初始化RETOBM旧账号集合。。。。')
    return


# 初始化交易码-交易码功能描述映射表
def INIT_TXNCDE_DICT():
    print(f'正在初始化交易码-交易码功能描述映射表。。。。')
    file_path = get_file_path('后台交易码-功能描述映射表.txt')
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip().split('|')
            TXNCDE_DICT[line[0]] = line[1].strip()
    print(f'完成初始化交易码-交易码功能描述映射表。。。。')
    return

# 初始化商户号-商户名称映射字典
def INIT_MERCH_DICT():
    print(f'正在初始化商户号-商户名称映射字典。。。。')
    file_path = get_file_path('商户号-商户名称映射表.txt')
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip().split('|')
            MERCH_DICT[line[0]] = line[1].strip()
    print(f'完成初始化商户号-商户名称映射字典。。。。')
    return


# 初始化新账号-核心客户号映射字典
def INIT_NEW_ACCOUNT_CUSID_DICT():
    print(f'正在初始化新账号-核心客户号映射字典。。。。')
    file_path = get_file_path('新账号-核心客户号映射表汇总.txt')
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip().split('|')
            NEW_ACCOUNT_CUSID_DICT[line[0]] = line[1].strip()
    print(f'完成初始化新账号-核心客户号映射字典。。。。')
    return
# 初始化机构号-机构号名称映射字典
def INIT_ORG_DICT():
    print(f'正在初始化机构号-机构号名称映射字典。。。。')
    file_path = get_file_path('机构号-机构名称映射表.txt')
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip().split('|')
            ORG_DICT[line[0]] = line[1].strip()
    print(f'完成初始化机构号-机构号名称映射字典。。。。')
    return
# 初始化账户号和卡表映射字典
def INIT_OLD_ACCOUNT_CARDNO_DICT ():
    print(f'正在初始化账户号和卡表映射字典。。。。')
    file_path = get_file_path('账户号-卡表映射表.txt')
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            parts = line.strip().split('|')
            if len(parts) == 2:
                OLD_ACCOUNT_CARDNO_DICT[line[0]] = line[1].strip()
            else:
                print("报错信息："+ parts[0])
    print(f'完成初始化账户号和卡表映射字典。。。。')
    return

def INIT_CDTDLG_MERCHNO_DICT():
    print(f'正在初始化CDTDLG映射字典。。。。')
    # 1. 输入文件路径
    line = input('请输入CDTDLG文件\n> ').strip()
    line = unicodedata.normalize('NFC', line).strip('\u202a\u202b\u200e\u200f')
    if not line:
        print('未检测到输入！')
        return
    # 解析路径
    raw_paths = [p.strip().strip('"').strip("'") for p in line.split()]
    files = [pathlib.Path(p) for p in raw_paths if os.path.isfile(p)]
    if not files:
        print('未检测到有效文件！')
        return
        # 4. 逐个处理文件
    for input_file in files:
        # 读取cdtdlg文件做映射关系表 cadnum:8 TLRNUM:3 txndat:5 systrc 23 ==》MCHNUM:14
        with open(input_file, 'r', encoding='utf-8') as f:
            print(input_file)
            records = f.readlines()
            for record in records:
                if '^ENDROW' not in record:
                    continue
                # print(record)
                cdtdlg = record.strip().split('|')
                cadnum = cdtdlg[8].strip()
                TLRNUM = cdtdlg[3].strip()
                txndat = cdtdlg[5].strip()
                if txndat != '':
                    txndat = txndat[:8]
                systrc = cdtdlg[23].strip()
                MCHNUM = cdtdlg[14].strip()
                # 判断是否有任意一个字段为空（空字符串 or 只有空格）
                if not all([cadnum, TLRNUM, txndat, systrc, MCHNUM]):
                    continue  # 有空值，跳过
                # print(cadnum + TLRNUM + txndat + systrc, MCHNUM)
                CDTDLG_MERCHNO_DICT[cadnum + TLRNUM + txndat + systrc] = MCHNUM
                # 测试
                # with open('商户名称码', 'a', encoding='utf-8') as f:
                #     f.write(f'{cadnum}{TLRNUM}{txndat}{systrc}|{MCHNUM}\n')
    print(f'完成初始化CDTDLG映射表。。。。')
    return


# 通过机构号获取原归属地
def get_addr(number):
    number = int(number)
    ret = ''
    if  sx_org_begin <= number <= sx_org_end :
        ret = '山西省'
    elif nm_org_begin <= number <= nm_org_end :
        ret =  '内蒙古省'
    elif jl_org_begin <= number <= jl_org_end :
        ret = '吉林省'
    elif hl_org_begin <= number <= hl_org_end :
        ret = '黑龙江省'
    return ret

def process_actvhh(actvhh_line):
    actvhh_line = actvhh_line.split('|')
    # 进行宽表数据加工
    # 宽表记录 在这里做宽表映射、写文件、入库等
    wide_line = [""] * 140
    # 数据解析
    # 1 客户号-二级-尽可能恢复 在ACTVHH表中第8个字段  CUSIDT
    ORGID3 = actvhh_line[7].strip()
    CUSIDT = actvhh_line[8].strip()
    APCODE = actvhh_line[9].strip()
    CURCDE = actvhh_line[10].strip()
    old_boknum = ORGID3+CUSIDT+APCODE+CURCDE
    new_boknum = OLDACCOUNT_NEWACCOUNT_DICT.get(old_boknum)
    # print(boknum)
    wide_line[0] = '000000000'
    CUSID = NEW_ACCOUNT_CUSID_DICT.get(new_boknum)
    if CUSID is not None :
        wide_line[0] = CUSID
    #     -- 账号类型-一级-必须恢复 - 待处理
    # 2 账号 使用actvhh_line表中ORGID3(7),CUSIDT(8),APCODE(9),CURCDE(10) 这四个字段拼接而成
    # 如果新账号存在，就替换；否则保留旧账号（或留空，按业务需求）
    wide_line[1] = old_boknum
    if new_boknum is not None :
        wide_line[1] = new_boknum
    # else:
    #     print(f"⚠️ 警告：账号 {wide_line[1]} 未在对照表中找到，保留原值")
    # 3 旧帐号
    wide_line[2] = old_boknum
    # 4 账号类型 -- 一级-必须恢复
    wide_line[3] = "01"
    # 5 查询卡号 -- 一级-必须恢复
    wide_line[4] = '00000'
    # 6 卡类型 -- 二级-尽可能恢复
    wide_line[5] = '99'
    # 7 机构号 -- 二级-尽可能恢复  orgidt
    wide_line[6] = actvhh_line[1]
    # 8 交易类型 -- 二级-尽可能恢复
    # 9 借贷标记  txnamt -- 一级-必须恢复
    if float(actvhh_line[11]) <= 0:
        wide_line[8] = 'D'
    else:
        wide_line[8] = 'C'
    # 10 币种 curcde -- 一级-必须恢复
    wide_line[9] = CurrencyDictionary.get(actvhh_line[10], 'XXX')
    # 11  -- 交易金额-一级-必须恢复
    wide_line[10] = f"{round(float(actvhh_line[11]), 2):.2f}"
    # 12 -- 交易余额-二级-尽可能恢复
    wide_line[11] = f"{round(float(actvhh_line[12]), 2):.2f}"
    # 13  -- 交易时间-一级-必须恢复
    global DataStartDate , DataEndDate
    ERYDAT = actvhh_line[5].strip()
    if ERYDAT[:8] < DataStartDate or DataStartDate == '':
        DataStartDate = ERYDAT[:8]
    if ERYDAT[:8] > DataEndDate or DataEndDate == '' :
        DataEndDate = ERYDAT[:8]
    # 日期串  yyyymmdd
    vchtim = actvhh_line[29].strip()
    # 时间串  hh:mi:ss 或 BATCH/None
    # 1. 日期统一转成 yyyyMMdd
    # date_part = datetime.strptime(vchdat, "%Y-%m-%d").strftime("%Y%m%d")
    date_part = datetime.strptime(ERYDAT[:8], "%Y%m%d").strftime("%Y%m%d")
    # 2. 时间部分处理
    if vchtim is None or vchtim.upper() == 'BATCH':
        time_part = '000000'
    else:
        time_part = vchtim.replace(':', '')  # hh:mi:ss -> hhmiss
    # 3. 拼接
    wide_line[12] = date_part + time_part  # yyyyMMddhhmiss
    # 22 交易摘要 furinf --二级-尽可能恢复
    wide_line[21] = actvhh_line[15]
    # 23 -- 交易网点名称-二级-尽可能恢复 txnbank ==  actorg
    org_flag = actvhh_line[1] + actvhh_line[21]
    TRANBRANCHNAME = ORG_DICT.get(org_flag)
    if TRANBRANCHNAME is not None:
        wide_line[22] = TRANBRANCHNAME
    # print(f'交易网点名称：{actvhh_line[1]}')
    # 24 -- 日志号-二级-尽可能恢复 VCHSET
    wide_line[23] = actvhh_line[3]
    # 35 -- 交易柜员号-二级-尽可能恢复 TLRNUM
    wide_line[34] = actvhh_line[2]
    # 37 -- 本方账号-二级-尽可能恢复vhh.orgid3,vhh.cusidt,vhh.apcode,vhh.curcde
    wide_line[36] = old_boknum
    # 46 -- 定存起始日期-三级-可为空 valdat
    val = actvhh_line[13].strip()
    if val is None or (isinstance(val, str) and val.strip().upper() == 'NULL') or val == '':
        wide_line[45] = ""  # 或 ''，根据你的需求
    else:
        try:
            # 尝试解析日期时间字符串
            dt = datetime.strptime(val.strip(), '%Y-%m-%d %H:%M:%S')
            wide_line[45] = dt.strftime('%Y-%m-%d')  # 转为 'YYYY-MM-DD'
        except (ValueError, AttributeError, TypeError):
            # 如果格式不对或不是字符串，保留原样或设为 None
            wide_line[45] = ""
    # 86 -- 输入当天套顺序号 vchset
    wide_line[85] = actvhh_line[3]
    # 87 -- 输入当天套内顺序号 setseq
    wide_line[86] = actvhh_line[4]
    # 88 -- 记帐日 erydat
    wide_line[87] = actvhh_line[5]
    # 90 -- 建卡销卡凭证编号 regnum
    wide_line[49] = actvhh_line[22]
    # 91-- 传票输入方式 erytyp
    wide_line[90] = actvhh_line[27]
    # 117 交易本方核算码 apcode
    wide_line[116] = actvhh_line[9]
    # 118 -- 交易本方货币码-三级-可为空  curcde
    wide_line[117] = CurrencyDictionary.get(actvhh_line[10], 'XXX')
    # 127 --源入湖物理文件名  -- 源入湖物理文件名-一级-必须恢复
    wide_line[126] = 'ACTVHH'
    # 128 --源入湖表名 -- 源入湖表名-一级-必须恢复
    wide_line[127] = 'ACTVHH'
    # 129 --源系统 -- 源系统-一级-必须恢复
    wide_line[128] = 'SBS/SBSP'
    # 130 --源归属地编码 -- 源归属地编码-一级-必须恢复
    wide_line[129] = addr_to_org.get(get_addr(actvhh_line[1]))
    # 131 --源归属地     -- 源归属地-一级-必须恢复
    wide_line[130] = get_addr(actvhh_line[1])
    # 132 --源时间节点
    wide_line[131] = '20100615'
    # 133 --数据来源
    wide_line[132] = 'ODMS'
    # 134 -- 交易记录属性-一级-必须恢复：（对公“01”，对私“02”）
    wide_line[133] = '01'
    # 138 跨行标识
    wide_line[137] = '2'
    # 139 -- 交易动账标识-一级-必须恢复 TXNAMT
    if actvhh_line[15].strip() == '':
        wide_line[138] = '0'
    else:
        wide_line[138] = '1'
    # 140 系统时间
    amnd_date = datetime.now().strftime('%Y%m%d%H%M%S')
    wide_line[139] = amnd_date
    # print("^A|^A".join(wide_line) + "^A|^A")
    return f"^A|^A".join(wide_line) + "^A|^A\n"


def process_rettxn(rettxn_line):
    rettxn_line = rettxn_line.split('|')
    # 进行宽表数据加工
    # 宽表记录 在这里做宽表映射、写文件、入库等
    wide_line = [""] * 140
    # 数据解析
    boknum = rettxn_line[1]  # 保存rettxn旧帐号
    boknum_len = len(boknum)
    # 获取旧线主账号 orgidt||depnum||dpttyp||seqnum||chkbit
    if boknum_len == 19 :
        old_account = boknum
        value = RETOBM_OLDACCOUNT_DICT.get(boknum)
        if  value is not None:
            old_account = value
    elif boknum_len == 18 :
        old_account = boknum
    elif boknum_len == 14 :
        old_account = rettxn_line[15]+boknum
    # 获取新线账号
    if boknum_len == 19:
        ACCNO = OLDACCOUNT_NEWACCOUNT_DICT.get(old_account)
        if ACCNO is None:
            ACCNO = old_account
    else:
        ACCNO = old_account
    # 1 客户号-二级-尽可能恢复
    wide_line[0] = '000000000'
    if boknum_len == 19:
        customer = NEW_ACCOUNT_CUSID_DICT.get(ACCNO)
    if boknum_len == 14 :
        customer = boknum[:7]
    if boknum_len == 18 :
        customer = boknum[4:11]
    if customer is not None:
        wide_line[0] = customer
    # 2 账号   -- 账号类型-一级-必须恢复 - 待处理
    wide_line[1] = ACCNO
    # 3 旧帐号 存取2 中的内容
    wide_line[2] = old_account
    # 4 账号类型 -- 一级-必须恢复
    if boknum_len == 19:
        ACCFLAG = '05' if boknum[9:11] == '04' else '03' if boknum[9] == '0' else '04'
    else:
        ACCFLAG = '01'
    wide_line[3] = ACCFLAG
    # 5 查询卡号 -- 一级-必须恢复
    wide_line[4] = '00000'
    # 获取查询卡号
    CARDNO = OLD_ACCOUNT_CARDNO_DICT.get(old_account)
    if CARDNO is not None:
        wide_line[4] = CARDNO
    # 6 卡类型 -- 二级-尽可能恢复
    wide_line[5] = '99'
    if wide_line[4] != '00000':
        wide_line[5] = '05'
    # 7 机构号 -- 二级-尽可能恢复  orgidt
    ORGNO= rettxn_line[13].strip()
    if ORGNO == '':
        ORGNO = rettxn_line[15].strip()
    wide_line[6] = ORGNO
    # 8 交易类型 -- 二级-尽可能恢复
    txncde = rettxn_line[10].strip()
    TRAN_TYPE = TXNCDE_DICT.get(txncde)
    if TRAN_TYPE is not None:
        wide_line[7] = TRAN_TYPE
    # 9 借贷标记  txnamt -- 一级-必须恢复
    wide_line[8] = 'D'
    dramnt = float(rettxn_line[19].strip())
    cramnt = float(rettxn_line[20].strip())
    prebal = float(rettxn_line[21].strip())
    pstbal = float(rettxn_line[22].strip())
    if dramnt == 0 and cramnt > 0 :
        wide_line[9] = 'C'
    elif dramnt < 0 and cramnt > 0 and prebal < pstbal:
        wide_line[9] = 'C'
    # 10 币种 curcde -- 一级-必须恢复
    if boknum_len == 19:
        currency = boknum[6:9]
    else:
        currency = boknum[-3:]
    wide_line[9] = CurrencyDictionary.get(currency, 'XXX')
    # 11  -- 交易金额-一级-必须恢复
    wide_line[10] = '100.00' # 默认使用 100.00
    TRANAMOUNT = dramnt + cramnt
    wide_line[10] = f"{round(float(TRANAMOUNT), 2):.2f}"
    # 12 -- 交易余额-二级-尽可能恢复
    wide_line[11] = '100.00'  # 默认使用 100.00
    wide_line[11] = f"{round(float(pstbal), 2):.2f}"
    # 13  -- 交易时间-一级-必须恢复
    wide_line[12] = rettxn_line[2][:14]  # yyyyMMddhhmiss
    global DataStartDate, DataEndDate
    TRADATE = rettxn_line[2][:8].strip()
    if TRADATE < DataStartDate or DataStartDate == '':
        DataStartDate =TRADATE
    if TRADATE > DataEndDate or DataEndDate == '':
        DataEndDate = TRADATE
    # 14 交易流水号 --二级-尽可能恢复
    wide_line[13] = rettxn_line[5]
    # 15 交易代码 -- 二级-尽可能恢复
    wide_line[14] = rettxn_line[10]
    # 22 交易摘要 TXNREF --二级-尽可能恢复
    wide_line[21] = rettxn_line[35]
    # 23 -- 交易网点名称-二级-尽可能恢复
    # 获取机构号+部门号
    ACTORG = rettxn_line[15].strip()
    ACTDEP = rettxn_line[16].strip()
    ORGNO_FLAG = ACTORG + ACTDEP
    # print(ORGNO_FLAG)
    TRAN_TYPE = ORG_DICT.get(ORGNO_FLAG)
    # print(TRAN_TYPE)
    if TRAN_TYPE is not None:
        wide_line[22] = TRAN_TYPE
    # print(f'交易网点名称：{rettxn_line[1]}')
    # 24 -- 日志号-二级-尽可能恢复 VCHSET
    wide_line[23] = rettxn_line[12]
    # 25 凭证种类 -- 二级-尽可能恢复
    wide_line[24] = rettxn_line[25]
    # 26 凭证号 -- 二级-尽可能恢复
    wide_line[25] = rettxn_line[26]
    # 27 现金标识 -- 二级-尽可能恢复 -- 现金标志 0：非现金  1：现金
    cshflg = rettxn_line[32].strip()
    if cshflg == '0':
        cshflg = '00'
    elif cshflg == '1':
        cshflg = '01'
    else:
        cshflg = ''
    wide_line[26] = cshflg
    # 28 终端号 -- 二级-尽可能恢复
    wide_line[27] = rettxn_line[4]
    # 29 交易是否成功 -- 二级-尽可能恢复
    wide_line[28] = rettxn_line[28]
    # 31 商户名称 -- 二级-尽可能恢复
    cadnum = rettxn_line[7].strip()
    TLRNUM = rettxn_line[17].strip()
    txndat = rettxn_line[2].strip()
    if txndat != '':
        txndat = txndat[:8].strip()
    systrc = str(rettxn_line[5].strip()).zfill(6)
    # 判断是否有任意一个字段为空（空字符串 or 只有空格）
    MERCHNO = ''
    if all([cadnum, TLRNUM, txndat, systrc]):
        cdtdlg_macth = cadnum + TLRNUM + txndat + systrc
        MERCHNO = CDTDLG_MERCHNO_DICT.get(cdtdlg_macth)
        MERCHANTNAME = MERCH_DICT.get(MERCHNO)
        if MERCHANTNAME is not None:
            wide_line[30] = MERCHANTNAME
    # 需要匹配cdtdlg
    # 32 商户号 -- 二级-尽可能恢复
    if MERCHNO is not None and MERCHNO != '':
        wide_line[31] = MERCHNO
    # 35 -- 交易柜员号-二级-尽可能恢复 TLRNUM
    wide_line[34] = rettxn_line[17]
    # 37 -- 本方账号-二级-尽可能恢复
    wide_line[36] = rettxn_line[7]
    # 38 -- 本方卡号-- 二级-尽可能恢复 -- 同查询卡号
    wide_line[37] = wide_line[4]
    # 60 支票类型 -- 三级-可为空
    wide_line[59] = rettxn_line[25]
    # 79 存折状态 -- 三级-可为空
    wide_line[78] = rettxn_line[31]
    # 89 存款账号 -- 三级-可为空
    wide_line[88] = boknum
    # 92 总利息 -- 三级-可为空
    wide_line[91] = f"{round(float(rettxn_line[23]), 2):.2f}"
    # 93 手续费 -- 三级-可为空
    wide_line[92] = f"{round(float(rettxn_line[24]), 2):.2f}"
    # 94 支票号 -- 三级-可为空
    wide_line[93] = rettxn_line[26]
    # 127 --源入湖物理文件名  -- 源入湖物理文件名-一级-必须恢复
    wide_line[126] = 'rettxn'
    # 128 --源入湖表名 -- 源入湖表名-一级-必须恢复
    wide_line[127] = 'rettxn'
    # 129 --源系统 -- 源系统-一级-必须恢复
    wide_line[128] = 'SBS/SBSP'
    # 130 --源归属地编码 -- 源归属地编码-一级-必须恢复
    wide_line[129] = addr_to_org.get(get_addr(ORGNO))
    # 131 --源归属地     -- 源归属地-一级-必须恢复
    wide_line[130] = get_addr(ORGNO)
    # 132 --源时间节点
    wide_line[131] = '20100615'
    # 133 --数据来源
    wide_line[132] = 'ODMS'
    # 134 -- 交易记录属性-一级-必须恢复：（对公“01”，对私“02”）
    if boknum_len == 19:
        wide_line[133] = '02'
    else:
        wide_line[133] = '02'
    # 138 跨行标识
    wide_line[137] = '2'
    # 139 -- 交易动账标识-一级-必须恢复 TXNAMT
    wide_line[138] = '1'
    # 140 系统时间
    amnd_date = datetime.now().strftime('%Y%m%d%H%M%S')
    wide_line[139] = amnd_date
    # print("^A|^A".join(wide_line) + "^A|^A")
    return "^A|^A".join(wide_line) + "^A|^A\n"


MechanismID_DICT = {
    '山西': '000005',
    '内蒙':'000006',
    '吉林':'000007',
    '黑龙江':'000009'
}
AssemblyID_DICT = {
    '山西':'NEW_RSX00_p6',
    '内蒙':'NEW_NM00_P6',
    '吉林':'NEW_JL00_P6',
    '黑龙江':'NEW_HL00_P6'
}

index_out_file = ''
# -------------------------------
# 可配置变量（你可以从外部传入）
# -------------------------------
PROVINCE = ''
RecNum = 0                    # 也可以动态设置，如 len(data)
DataStartDate = ''
DataEndDate = ''
def get_contrl_line():
    # -------------------------------
    # 构造控制信息字典
    # -------------------------------
    control_info = {
        "TabName": "dep_trade_pro",
        "AssemblyID": AssemblyID_DICT.get(PROVINCE),
        "PlatformID": "P6",
        "IsSplitFlag": "0",
        "MechanismID": MechanismID_DICT.get(PROVINCE),
        "DataStartDate": DataStartDate,
        "DataEndDate": DataEndDate,
        "IncID": "1",
        "RecNum": RecNum,
        "Sep": "^A|^A",
        "CycFlag": "Y",
        "TableGenType": "0",
        "GenTime": datetime.now().strftime("%Y%m%d %H:%M:%S:00")
    }
    # -------------------------------
    # 生成 JSON 字符串（紧凑格式）
    # -------------------------------
    json_str = json.dumps(control_info, separators=(',', ':'), ensure_ascii=False)
    # -------------------------------
    # 生成完整控制行
    # -------------------------------
    control_line = f"|||diip-control|||{json_str}\n"

    return control_line

def process_file(input_path):
    # 读取input_path有效数据
    wide_lines = []
    yc_lines = []
    sum = 0
    with open(input_path, 'r', encoding='utf-8') as f:
        lines = deque(f)
        while lines:
            record = lines.popleft()
            if record in ('|||BEGIN|||', '|||END|||'):
                continue
            if not record.rstrip().endswith('^ENDROW'):
                continue
            # print(f'RETTXN长度: {len(record.split('|'))}')
            if len(record.strip().split('|')) == ACTVHH_LEN:
                wide_lines.append(process_actvhh(record))
                sum += 1
            elif len(record.strip().split('|')) == RETTXN_LEN:
                wide_lines.append(process_rettxn(record))
                sum += 1
            else:
                yc_lines.append(record)
    print(f'一共统计处理数据：{sum}')
    global RecNum , index_out_file
    RecNum = RecNum + sum

    # 写入异常数据
    file_path = pathlib.Path(output_path)
    file_path.mkdir(parents=True, exist_ok=True)
    index_out_file = file_path / output_file
    with open(file_path/'异常数据.txt', 'a', encoding='utf-8') as f:
        f.writelines(yc_lines)
    # 追加写入宽表数据
    with open(file_path/output_file, 'a', encoding='utf-8') as f:
        f.writelines(wide_lines)
    return

# 递归扫描路径下的所有文件
def scan_files_recursive(root_path ):
    root = pathlib.Path(root_path)
    all_files = []
    for path_item in root.rglob('*'):
        # 获取文本文件并且跳过配置文件
        if path_item.is_file() and path_item.suffix.lower() != '.xml':
            all_files.append(str(path_item.resolve()))

    for file in all_files:
        print(f'正在处理文件：{file}')
        process_file(file)
        print(f'处理完成文件：{file}')
    # 写入控制行信息
        # 追加写入宽表数据
    with open(index_out_file, 'a', encoding='utf-8') as f:
        f.write(get_contrl_line())
    return 0



def main():
    global output_path , output_file , PROVINCE
    # 获取输入目录
    input_path = input("输入处理文件目录: ").strip()
    # 获取输出目录
    output_path = input('输入文件输出路径：').strip()
    # 获取输出文件名称
    PROVINCE = input('输入山西/吉林/内蒙/黑龙江：').strip()
    output_file = outfile_dict.get(PROVINCE)
    if output_file is None:
        print('输入错误重新输入数据。。。。。。。')
        return
    # 初始化新旧账号对照表
    INIT_OLDACCOUNT_NEWACCOUNT_DICT()
    # 初始化核心客户号
    INIT_NEW_ACCOUNT_CUSID_DICT()
    # 初始化旧账号映射表
    INIT_RETOBM_OLDACCOUNT_SET()
    # 初始化机构号-机构号名称映射表
    INIT_ORG_DICT()
    # 初始化查询卡号
    INIT_OLD_ACCOUNT_CARDNO_DICT()
    # 初始化交易类型映射表
    INIT_TXNCDE_DICT()
    # 初始化商户号
    INIT_MERCH_DICT()
    # 初始化卡交易流水 cdtdlg
    INIT_CDTDLG_MERCHNO_DICT()
    # 初始化全局变量
    input_path = unicodedata.normalize('NFC', input_path).strip('\u202a\u202b\u200e\u200f')
    scan_files_recursive(input_path )

    return 0


if __name__ == '__main__':
    main()